home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / daemons / ftpd / RCS / ftpcmd.y,v < prev    next >
Encoding:
Text File  |  1992-08-11  |  18.9 KB  |  1,077 lines

  1. head     1.5;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    shirriff:1.5; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.5
  10. date     92.08.10.17.08.17;  author shirriff;  state Exp;
  11. branches ;
  12. next     1.4;
  13.  
  14. 1.4
  15. date     92.02.26.11.45.10;  author shirriff;  state Exp;
  16. branches ;
  17. next     1.3;
  18.  
  19. 1.3
  20. date     91.09.16.20.58.17;  author shirriff;  state Exp;
  21. branches ;
  22. next     1.2;
  23.  
  24. 1.2
  25. date     90.11.12.16.25.52;  author shirriff;  state Exp;
  26. branches ;
  27. next     1.1;
  28.  
  29. 1.1
  30. date     89.01.18.16.06.36;  author douglis;  state Exp;
  31. branches ;
  32. next     ;
  33.  
  34.  
  35. desc
  36. @grammar for ftp commands.
  37. @
  38.  
  39.  
  40. 1.5
  41. log
  42. @Fixed problems with daemon crashing if \n was missing.
  43. @
  44. text
  45. @/*
  46.  * Copyright (c) 1985 Regents of the University of California.
  47.  * All rights reserved.
  48.  *
  49.  * Redistribution and use in source and binary forms are permitted
  50.  * provided that this notice is preserved and that due credit is given
  51.  * to the University of California at Berkeley. The name of the University
  52.  * may not be used to endorse or promote products derived from this
  53.  * software without specific prior written permission. This software
  54.  * is provided ``as is'' without express or implied warranty.
  55.  */
  56.  
  57. /*
  58.  * Grammar for FTP commands.
  59.  * See RFC 765.
  60.  */
  61.  
  62. %{
  63.  
  64. #ifndef lint
  65. static char sccsid[] = "@@(#)ftpcmd.y    5.10 (Berkeley) 3/14/88";
  66. #endif /* not lint */
  67.  
  68. #include <sys/types.h>
  69. #include <sys/socket.h>
  70.  
  71. #include <netinet/in.h>
  72.  
  73. #include <arpa/ftp.h>
  74.  
  75. #include <stdio.h>
  76. #include <signal.h>
  77. #include <ctype.h>
  78. #include <pwd.h>
  79. #include <setjmp.h>
  80. #include <syslog.h>
  81.  
  82. extern    struct sockaddr_in data_dest;
  83. extern    int logged_in;
  84. extern    struct passwd *pw;
  85. extern    int guest;
  86. extern    int logging;
  87. extern    int type;
  88. extern    int form;
  89. extern    int debug;
  90. extern    int timeout;
  91. extern  int pdata;
  92. extern    char hostname[];
  93. extern    char *globerr;
  94. extern    int usedefault;
  95. extern    int unique;
  96. extern  int transflag;
  97. extern  char tmpline[];
  98. char    **glob();
  99. extern    int guestok();
  100.  
  101. static    int cmd_type;
  102. static    int cmd_form;
  103. static    int cmd_bytesz;
  104. char cbuf[512];
  105. char *fromname;
  106.  
  107. char    *index();
  108. %}
  109.  
  110. %token
  111.     A    B    C    E    F    I
  112.     L    N    P    R    S    T
  113.  
  114.     SP    CRLF    COMMA    STRING    NUMBER
  115.  
  116.     USER    PASS    ACCT    REIN    QUIT    PORT
  117.     PASV    TYPE    STRU    MODE    RETR    STOR
  118.     APPE    MLFL    MAIL    MSND    MSOM    MSAM
  119.     MRSQ    MRCP    ALLO    REST    RNFR    RNTO
  120.     ABOR    DELE    CWD    LIST    NLST    SITE
  121.     STAT    HELP    NOOP    XMKD    XRMD    XPWD
  122.     XCUP    STOU
  123.  
  124.     LEXERR
  125.  
  126. %start    cmd_list
  127.  
  128. %%
  129.  
  130. cmd_list:    /* empty */
  131.     |    cmd_list cmd
  132.         = {
  133.             fromname = (char *) 0;
  134.         }
  135.     |    cmd_list rcmd
  136.     ;
  137.  
  138. cmd:        USER SP username CRLF
  139.         = {
  140.             extern struct passwd *getpwnam();
  141.  
  142.             logged_in = 0;
  143.             if (strcmp((char *) $3, "ftp") == 0 ||
  144.               strcmp((char *) $3, "anonymous") == 0) {
  145.                 if ((pw = getpwnam("ftp")) != NULL) {
  146.                     guest = 1;
  147.                     reply(331,
  148.                   "Guest login ok; supply userid as password.");
  149.                 }
  150.                 else {
  151.                     reply(530, "User %s unknown.", $3);
  152.                 }
  153.             } else if (checkuser((char *) $3)) {
  154.                 guest = 0;
  155.                 pw = getpwnam((char *) $3);
  156.                 if (pw == NULL) {
  157.                     reply(530, "User %s unknown.", $3);
  158.                 }
  159.                 else {
  160.                     reply(331, "Password required for %s.", $3);
  161.                 }
  162.             } else {
  163.                 reply(530, "User %s access denied.", $3);
  164.             }
  165.             free((char *) $3);
  166.         }
  167.     |    PASS SP password CRLF
  168.         = {
  169.             pass((char *) $3);
  170.             free((char *) $3);
  171.         }
  172.     |    PORT SP host_port CRLF
  173.         = {
  174.             usedefault = 0;
  175.             if (pdata > 0) {
  176.                 (void) close(pdata);
  177.             }
  178.             pdata = -1;
  179.             reply(200, "PORT command successful.");
  180.         }
  181.     |    PASV CRLF
  182.         = {
  183.             passive();
  184.         }
  185.     |    TYPE SP type_code CRLF
  186.         = {
  187.             switch (cmd_type) {
  188.  
  189.             case TYPE_A:
  190.                 if (cmd_form == FORM_N) {
  191.                     reply(200, "Type set to A.");
  192.                     type = cmd_type;
  193.                     form = cmd_form;
  194.                 } else
  195.                     reply(504, "Form must be N.");
  196.                 break;
  197.  
  198.             case TYPE_E:
  199.                 reply(504, "Type E not implemented.");
  200.                 break;
  201.  
  202.             case TYPE_I:
  203.                 reply(200, "Type set to I.");
  204.                 type = cmd_type;
  205.                 break;
  206.  
  207.             case TYPE_L:
  208.                 if (cmd_bytesz == 8) {
  209.                     reply(200,
  210.                         "Type set to L (byte size 8).");
  211.                     type = cmd_type;
  212.                 } else
  213.                     reply(504, "Byte size must be 8.");
  214.             }
  215.         }
  216.     |    STRU SP struct_code CRLF
  217.         = {
  218.             switch ($3) {
  219.  
  220.             case STRU_F:
  221.                 reply(200, "STRU F ok.");
  222.                 break;
  223.  
  224.             default:
  225.                 reply(504, "Unimplemented STRU type.");
  226.             }
  227.         }
  228.     |    MODE SP mode_code CRLF
  229.         = {
  230.             switch ($3) {
  231.  
  232.             case MODE_S:
  233.                 reply(200, "MODE S ok.");
  234.                 break;
  235.  
  236.             default:
  237.                 reply(502, "Unimplemented MODE type.");
  238.             }
  239.         }
  240.     |    ALLO SP NUMBER CRLF
  241.         = {
  242.             reply(202, "ALLO command ignored.");
  243.         }
  244.     |    RETR check_login SP pathname_perm CRLF
  245.         = {
  246.             if ($2 && $4 != NULL)
  247.                 retrieve((char *) 0, (char *) $4);
  248.             if ($4 != NULL)
  249.                 free((char *) $4);
  250.         }
  251. /*
  252.  * To allow guests to put files, change check_login_guest to check_login.
  253.  */
  254.     |    STOR check_login_guest SP pathname_perm CRLF
  255.         = {
  256.             if ($2 && $4 != NULL)
  257.                 store((char *) $4, "w");
  258.             if ($4 != NULL)
  259.                 free((char *) $4);
  260.         }
  261.     |    APPE check_login_guest SP pathname_perm CRLF
  262.         = {
  263.             if ($2 && $4 != NULL)
  264.                 store((char *) $4, "a");
  265.             if ($4 != NULL)
  266.                 free((char *) $4);
  267.         }
  268.     |    NLST check_login CRLF
  269.         = {
  270.             if ($2)
  271.                 retrieve("/bin/ls", "");
  272.         }
  273.     |    NLST check_login SP pathname_perm CRLF
  274.         = {
  275.             if ($2 && $4 != NULL)
  276.                 retrieve("/bin/ls %s", (char *) $4);
  277.             if ($4 != NULL)
  278.                 free((char *) $4);
  279.         }
  280.     |    LIST check_login CRLF
  281.         = {
  282.             if ($2)
  283.                 retrieve("/bin/ls -lg", "");
  284.         }
  285.     |    LIST check_login SP pathname_perm CRLF
  286.         = {
  287.             if ($2 && $4 != NULL)
  288.                 retrieve("/bin/ls -lg %s", (char *) $4);
  289.             if ($4 != NULL)
  290.                 free((char *) $4);
  291.         }
  292.     |    DELE check_login_guest SP pathname_perm CRLF
  293.         = {
  294.             if ($2 && $4 != NULL)
  295.                 delete((char *) $4);
  296.             if ($4 != NULL)
  297.                 free((char *) $4);
  298.         }
  299.     |    RNTO SP pathname_perm CRLF
  300.         = {
  301.             if (fromname) {
  302.                 renamecmd(fromname, (char *) $3);
  303.                 free(fromname);
  304.                 fromname = (char *) 0;
  305.             } else {
  306.                 reply(503, "Bad sequence of commands.");
  307.             }
  308.             free((char *) $3);
  309.         }
  310.     |    ABOR CRLF
  311.         = {
  312.             reply(225, "ABOR command successful.");
  313.         }
  314.     |    CWD check_login CRLF
  315.         = {
  316.             if ($2)
  317.                 cwd(pw->pw_dir);
  318.         }
  319.     |    CWD check_login SP pathname CRLF
  320.         = {
  321.             if ($2 && $4 != NULL)
  322.                 cwd((char *) $4);
  323.             if ($4 != NULL)
  324.                 free((char *) $4);
  325.         }
  326.     |    HELP CRLF
  327.         = {
  328.             help((char *) 0);
  329.         }
  330.     |    HELP SP STRING CRLF
  331.         = {
  332.             help((char *) $3);
  333.         }
  334.     |    NOOP CRLF
  335.         = {
  336.             reply(200, "NOOP command successful.");
  337.         }
  338. /*
  339.  * To allow guests to put files, change check_login_guest to check_login.
  340.  */
  341.     |    XMKD check_login_guest SP pathname_perm CRLF
  342.         = {
  343.             if ($2 && $4 != NULL)
  344.                 makedir((char *) $4);
  345.             if ($4 != NULL)
  346.                 free((char *) $4);
  347.         }
  348.     |    XRMD check_login_guest SP pathname_perm CRLF
  349.         = {
  350.             if ($2 && $4 != NULL)
  351.                 removedir((char *) $4);
  352.             if ($4 != NULL)
  353.                 free((char *) $4);
  354.         }
  355.     |    XPWD check_login CRLF
  356.         = {
  357.             if ($2)
  358.                 pwd();
  359.         }
  360.     |    XCUP check_login CRLF
  361.         = {
  362.             if ($2)
  363.                 cwd("..");
  364.         }
  365.     |    STOU check_login_guest SP pathname_perm CRLF
  366.         = {
  367.             if ($2 && $4 != NULL) {
  368.                 unique++;
  369.                 store((char *) $4, "w");
  370.                 unique = 0;
  371.             }
  372.             if ($4 != NULL)
  373.                 free((char *) $4);
  374.         }
  375.     |    QUIT CRLF
  376.         = {
  377.             reply(221, "Goodbye.");
  378.             dologout(0);
  379.         }
  380.     |    error CRLF
  381.         = {
  382.             yyerrok;
  383.         }
  384.     ;
  385.  
  386. rcmd:        RNFR check_login_guest SP pathname_perm CRLF
  387.         = {
  388.             char *renamefrom();
  389.  
  390.             if ($2 && $4) {
  391.                 fromname = renamefrom((char *) $4);
  392.                 if (fromname == (char *) 0 && $4) {
  393.                     free((char *) $4);
  394.                 }
  395.             }
  396.         }
  397.     ;
  398.         
  399. username:    STRING
  400.     ;
  401.  
  402. password:    STRING
  403.     ;
  404.  
  405. byte_size:    NUMBER
  406.     ;
  407.  
  408. host_port:    NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 
  409.         NUMBER COMMA NUMBER
  410.         = {
  411.             register char *a, *p;
  412.  
  413.             a = (char *)&data_dest.sin_addr;
  414.             a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
  415.             p = (char *)&data_dest.sin_port;
  416.             p[0] = $9; p[1] = $11;
  417.             data_dest.sin_family = AF_INET;
  418.         }
  419.     ;
  420.  
  421. form_code:    N
  422.     = {
  423.         $$ = FORM_N;
  424.     }
  425.     |    T
  426.     = {
  427.         $$ = FORM_T;
  428.     }
  429.     |    C
  430.     = {
  431.         $$ = FORM_C;
  432.     }
  433.     ;
  434.  
  435. type_code:    A
  436.     = {
  437.         cmd_type = TYPE_A;
  438.         cmd_form = FORM_N;
  439.     }
  440.     |    A SP form_code
  441.     = {
  442.         cmd_type = TYPE_A;
  443.         cmd_form = $3;
  444.     }
  445.     |    E
  446.     = {
  447.         cmd_type = TYPE_E;
  448.         cmd_form = FORM_N;
  449.     }
  450.     |    E SP form_code
  451.     = {
  452.         cmd_type = TYPE_E;
  453.         cmd_form = $3;
  454.     }
  455.     |    I
  456.     = {
  457.         cmd_type = TYPE_I;
  458.     }
  459.     |    L
  460.     = {
  461.         cmd_type = TYPE_L;
  462.         cmd_bytesz = 8;
  463.     }
  464.     |    L SP byte_size
  465.     = {
  466.         cmd_type = TYPE_L;
  467.         cmd_bytesz = $3;
  468.     }
  469.     /* this is for a bug in the BBN ftp */
  470.     |    L byte_size
  471.     = {
  472.         cmd_type = TYPE_L;
  473.         cmd_bytesz = $2;
  474.     }
  475.     ;
  476.  
  477. struct_code:    F
  478.     = {
  479.         $$ = STRU_F;
  480.     }
  481.     |    R
  482.     = {
  483.         $$ = STRU_R;
  484.     }
  485.     |    P
  486.     = {
  487.         $$ = STRU_P;
  488.     }
  489.     ;
  490.  
  491. mode_code:    S
  492.     = {
  493.         $$ = MODE_S;
  494.     }
  495.     |    B
  496.     = {
  497.         $$ = MODE_B;
  498.     }
  499.     |    C
  500.     = {
  501.         $$ = MODE_C;
  502.     }
  503.     ;
  504.  
  505. pathname:    pathstring
  506.     = {
  507.         /*
  508.          * Problem: this production is used for all pathname
  509.          * processing, but only gives a 550 error reply.
  510.          * This is a valid reply in some cases but not in others.
  511.          */
  512.         if ($1 && strncmp((char *) $1, "~", 1) == 0) {
  513.             $$ = (int)*glob((char *) $1);
  514.             if (globerr != NULL) {
  515.                 reply(550, globerr);
  516.                 $$ = NULL;
  517.             }
  518.             free((char *) $1);
  519.         } else
  520.             $$ = $1;
  521.     }
  522.     ;
  523.  
  524. pathname_perm:    pathname
  525.     = {
  526.         /*
  527.          * Check that guest's path can't access anything bad.
  528.          */
  529.         if (guest && $1 && (strstr($1,"/..") ||
  530.             (((char *)$1)[0]=='.'&&((char *)$1)[1]=='.'))) {
  531.             reply(550, "Sorry, guest can't use .. in paths.");
  532.             log("Bad path: %s\n", $1);
  533.             $$ = NULL;
  534.         } else {
  535.             $$ = $1;
  536.         }
  537.     }
  538.     ;
  539.  
  540. pathstring:    STRING
  541.     ;
  542.  
  543. check_login_guest:    /* empty */
  544.     = {
  545.         if (logged_in) {
  546.             $$ = 1;
  547.             if (guest) {
  548.                 reply(553, "Sorry, guest can't do that.");
  549.                 log("Bad operation\n");
  550.                 $$ = 0;
  551.             }
  552.         } else {
  553.             reply(530, "Please login with USER and PASS.");
  554.             $$ = 0;
  555.         }
  556.     }
  557.     ;
  558.  
  559. check_login:    /* empty */
  560.     = {
  561.         if (logged_in) {
  562.             $$ = 1;
  563.         } else {
  564.             reply(530, "Please login with USER and PASS.");
  565.             $$ = 0;
  566.         }
  567.     }
  568.     ;
  569.  
  570. %%
  571.  
  572. extern jmp_buf errcatch;
  573.  
  574. #define    CMD    0    /* beginning of command */
  575. #define    ARGS    1    /* expect miscellaneous arguments */
  576. #define    STR1    2    /* expect SP followed by STRING */
  577. #define    STR2    3    /* expect STRING */
  578. #define    OSTR    4    /* optional STRING */
  579.  
  580. struct tab {
  581.     char    *name;
  582.     short    token;
  583.     short    state;
  584.     short    implemented;    /* 1 if command is implemented */
  585.     char    *help;
  586. };
  587.  
  588. struct tab cmdtab[] = {        /* In order defined in RFC 765 */
  589.     { "USER", USER, STR1, 1,    "<sp> username" },
  590.     { "PASS", PASS, STR1, 1,    "<sp> password" },
  591.     { "ACCT", ACCT, STR1, 0,    "(specify account)" },
  592.     { "REIN", REIN, ARGS, 0,    "(reinitialize server state)" },
  593.     { "QUIT", QUIT, ARGS, 1,    "(terminate service)", },
  594.     { "PORT", PORT, ARGS, 1,    "<sp> b0, b1, b2, b3, b4" },
  595.     { "PASV", PASV, ARGS, 1,    "(set server in passive mode)" },
  596.     { "TYPE", TYPE, ARGS, 1,    "<sp> [ A | E | I | L ]" },
  597.     { "STRU", STRU, ARGS, 1,    "(specify file structure)" },
  598.     { "MODE", MODE, ARGS, 1,    "(specify transfer mode)" },
  599.     { "RETR", RETR, STR1, 1,    "<sp> file-name" },
  600.     { "STOR", STOR, STR1, 1,    "<sp> file-name" },
  601.     { "APPE", APPE, STR1, 1,    "<sp> file-name" },
  602.     { "MLFL", MLFL, OSTR, 0,    "(mail file)" },
  603.     { "MAIL", MAIL, OSTR, 0,    "(mail to user)" },
  604.     { "MSND", MSND, OSTR, 0,    "(mail send to terminal)" },
  605.     { "MSOM", MSOM, OSTR, 0,    "(mail send to terminal or mailbox)" },
  606.     { "MSAM", MSAM, OSTR, 0,    "(mail send to terminal and mailbox)" },
  607.     { "MRSQ", MRSQ, OSTR, 0,    "(mail recipient scheme question)" },
  608.     { "MRCP", MRCP, STR1, 0,    "(mail recipient)" },
  609.     { "ALLO", ALLO, ARGS, 1,    "allocate storage (vacuously)" },
  610.     { "REST", REST, STR1, 0,    "(restart command)" },
  611.     { "RNFR", RNFR, STR1, 1,    "<sp> file-name" },
  612.     { "RNTO", RNTO, STR1, 1,    "<sp> file-name" },
  613.     { "ABOR", ABOR, ARGS, 1,    "(abort operation)" },
  614.     { "DELE", DELE, STR1, 1,    "<sp> file-name" },
  615.     { "CWD",  CWD,  OSTR, 1,    "[ <sp> directory-name]" },
  616.     { "XCWD", CWD,    OSTR, 1,    "[ <sp> directory-name ]" },
  617.     { "LIST", LIST, OSTR, 1,    "[ <sp> path-name ]" },
  618.     { "NLST", NLST, OSTR, 1,    "[ <sp> path-name ]" },
  619.     { "SITE", SITE, STR1, 0,    "(get site parameters)" },
  620.     { "STAT", STAT, OSTR, 0,    "(get server status)" },
  621.     { "HELP", HELP, OSTR, 1,    "[ <sp> <string> ]" },
  622.     { "NOOP", NOOP, ARGS, 1,    "" },
  623.     { "MKD",  XMKD, STR1, 1,    "<sp> path-name" },
  624.     { "XMKD", XMKD, STR1, 1,    "<sp> path-name" },
  625.     { "RMD",  XRMD, STR1, 1,    "<sp> path-name" },
  626.     { "XRMD", XRMD, STR1, 1,    "<sp> path-name" },
  627.     { "PWD",  XPWD, ARGS, 1,    "(return current directory)" },
  628.     { "XPWD", XPWD, ARGS, 1,    "(return current directory)" },
  629.     { "CDUP", XCUP, ARGS, 1,    "(change to parent directory)" },
  630.     { "XCUP", XCUP, ARGS, 1,    "(change to parent directory)" },
  631.     { "STOU", STOU, STR1, 1,    "<sp> file-name" },
  632.     { NULL,   0,    0,    0,    0 }
  633. };
  634.  
  635. struct tab *
  636. lookup(cmd)
  637.     char *cmd;
  638. {
  639.     register struct tab *p;
  640.  
  641.     for (p = cmdtab; p->name != NULL; p++)
  642.         if (strcmp(cmd, p->name) == 0)
  643.             return (p);
  644.     return (0);
  645. }
  646.  
  647. #include <arpa/telnet.h>
  648.  
  649. /*
  650.  * getline - a hacked up version of fgets to ignore TELNET escape codes.
  651.  */
  652. char *
  653. getline(s, n, iop)
  654.     char *s;
  655.     register FILE *iop;
  656. {
  657.     register c;
  658.     register char *cs;
  659.  
  660.     cs = s;
  661. /* tmpline may contain saved command from urgent mode interruption */
  662.     for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
  663.         *cs++ = tmpline[c];
  664.         if (tmpline[c] == '\n') {
  665.             *cs++ = '\0';
  666.             if (debug) {
  667.                 syslog(LOG_DEBUG, "FTPD: command: %s", s);
  668.             }
  669.             tmpline[0] = '\0';
  670.             return(s);
  671.         }
  672.         if (c == 0) {
  673.             tmpline[0] = '\0';
  674.         }
  675.     }
  676.     while (--n > 0 && (c = getc(iop)) != EOF) {
  677.         c = 0377 & c;
  678.         while (c == IAC) {
  679.             switch (c = 0377 & getc(iop)) {
  680.             case WILL:
  681.             case WONT:
  682.                 c = 0377 & getc(iop);
  683.                 printf("%c%c%c", IAC, WONT, c);
  684.                 (void) fflush(stdout);
  685.                 break;
  686.             case DO:
  687.             case DONT:
  688.                 c = 0377 & getc(iop);
  689.                 printf("%c%c%c", IAC, DONT, c);
  690.                 (void) fflush(stdout);
  691.                 break;
  692.             default:
  693.                 break;
  694.             }
  695.             c = 0377 & getc(iop); /* try next character */
  696.         }
  697.         *cs++ = c;
  698.         if (c=='\n')
  699.             break;
  700.     }
  701.     if (c == EOF && cs == s)
  702.         return (NULL);
  703.     *cs++ = '\0';
  704.     if (debug) {
  705.         syslog(LOG_DEBUG, "FTPD: command: %s", s);
  706.     }
  707.     return (s);
  708. }
  709.  
  710. static int
  711. toolong()
  712. {
  713.     time_t now;
  714.     extern char *ctime();
  715.     extern time_t time();
  716.  
  717.     reply(421,
  718.       "Timeout (%d seconds): closing control connection.", timeout);
  719.     (void) time(&now);
  720.     if (logging) {
  721.         syslog(LOG_INFO,
  722.             "FTPD: User %s timed out after %d seconds at %s",
  723.             (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
  724.     }
  725.     dologout(1);
  726. }
  727.  
  728. yylex()
  729. {
  730.     static int cpos, state;
  731.     register char *cp;
  732.     register struct tab *p;
  733.     int n;
  734.     char c;
  735.  
  736.     for (;;) {
  737.         switch (state) {
  738.  
  739.         case CMD:
  740.             (void) signal(SIGALRM, toolong);
  741.             (void) alarm((unsigned) timeout);
  742.             if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
  743.                 reply(221, "You could at least say goodbye.");
  744.                 dologout(0);
  745.             }
  746.             (void) alarm(0);
  747.             if (index(cbuf, '\r')) {
  748.                 cp = index(cbuf, '\r');
  749.                 cp[0] = '\n'; cp[1] = 0;
  750.             }
  751.             if (!index(cbuf, '\n')) {
  752.                 cbuf[0] = '\n';
  753.                 cbuf[1] = 0;
  754.             }
  755.             if (index(cbuf, ' '))
  756.                 cpos = index(cbuf, ' ') - cbuf;
  757.             else
  758.                 cpos = index(cbuf, '\n') - cbuf;
  759.             if (cpos == 0) {
  760.                 cpos = 4;
  761.             }
  762.             c = cbuf[cpos];
  763.             cbuf[cpos] = '\0';
  764.             upper(cbuf);
  765.             p = lookup(cbuf);
  766.             cbuf[cpos] = c;
  767.             if (p != 0) {
  768.                 if (p->implemented == 0) {
  769.                     nack(p->name);
  770.                     longjmp(errcatch,0);
  771.                     /* NOTREACHED */
  772.                 }
  773.                 state = p->state;
  774.                 yylval = (int) p->name;
  775.                 return (p->token);
  776.             }
  777.             break;
  778.  
  779.         case OSTR:
  780.             if (cbuf[cpos] == '\n') {
  781.                 state = CMD;
  782.                 return (CRLF);
  783.             }
  784.             /* FALL THRU */
  785.  
  786.         case STR1:
  787.             if (cbuf[cpos] == ' ') {
  788.                 cpos++;
  789.                 state = STR2;
  790.                 return (SP);
  791.             }
  792.             break;
  793.  
  794.         case STR2:
  795.             cp = &cbuf[cpos];
  796.             n = strlen(cp);
  797.             cpos += n - 1;
  798.             /*
  799.              * Make sure the string is nonempty and \n terminated.
  800.              */
  801.             if (n > 1 && cbuf[cpos] == '\n') {
  802.                 cbuf[cpos] = '\0';
  803.                 yylval = copy(cp);
  804.                 cbuf[cpos] = '\n';
  805.                 state = ARGS;
  806.                 return (STRING);
  807.             }
  808.             break;
  809.  
  810.         case ARGS:
  811.             if (isdigit(cbuf[cpos])) {
  812.                 cp = &cbuf[cpos];
  813.                 while (isdigit(cbuf[++cpos]))
  814.                     ;
  815.                 c = cbuf[cpos];
  816.                 cbuf[cpos] = '\0';
  817.                 yylval = atoi(cp);
  818.                 cbuf[cpos] = c;
  819.                 return (NUMBER);
  820.             }
  821.             switch (cbuf[cpos++]) {
  822.  
  823.             case '\n':
  824.                 state = CMD;
  825.                 return (CRLF);
  826.  
  827.             case ' ':
  828.                 return (SP);
  829.  
  830.             case ',':
  831.                 return (COMMA);
  832.  
  833.             case 'A':
  834.             case 'a':
  835.                 return (A);
  836.  
  837.             case 'B':
  838.             case 'b':
  839.                 return (B);
  840.  
  841.             case 'C':
  842.             case 'c':
  843.                 return (C);
  844.  
  845.             case 'E':
  846.             case 'e':
  847.                 return (E);
  848.  
  849.             case 'F':
  850.             case 'f':
  851.                 return (F);
  852.  
  853.             case 'I':
  854.             case 'i':
  855.                 return (I);
  856.  
  857.             case 'L':
  858.             case 'l':
  859.                 return (L);
  860.  
  861.             case 'N':
  862.             case 'n':
  863.                 return (N);
  864.  
  865.             case 'P':
  866.             case 'p':
  867.                 return (P);
  868.  
  869.             case 'R':
  870.             case 'r':
  871.                 return (R);
  872.  
  873.             case 'S':
  874.             case 's':
  875.                 return (S);
  876.  
  877.             case 'T':
  878.             case 't':
  879.                 return (T);
  880.  
  881.             }
  882.             break;
  883.  
  884.         default:
  885.             fatal("Unknown state in scanner.");
  886.         }
  887.         yyerror((char *) 0);
  888.         state = CMD;
  889.         longjmp(errcatch,0);
  890.     }
  891. }
  892.  
  893. upper(s)
  894.     char *s;
  895. {
  896.     while (*s != '\0') {
  897.         if (islower(*s))
  898.             *s = toupper(*s);
  899.         s++;
  900.     }
  901. }
  902.  
  903. copy(s)
  904.     char *s;
  905. {
  906.     char *p;
  907.     extern char *malloc(), *strcpy();
  908.  
  909.     p = malloc((unsigned) strlen(s) + 1);
  910.     if (p == NULL)
  911.         fatal("Ran out of memory.");
  912.     (void) strcpy(p, s);
  913.     return ((int)p);
  914. }
  915.  
  916. help(s)
  917.     char *s;
  918. {
  919.     register struct tab *c;
  920.     register int width, NCMDS;
  921.  
  922.     width = 0, NCMDS = 0;
  923.     for (c = cmdtab; c->name != NULL; c++) {
  924.         int len = strlen(c->name) + 1;
  925.  
  926.         if (len > width)
  927.             width = len;
  928.         NCMDS++;
  929.     }
  930.     width = (width + 8) &~ 7;
  931.     if (s == 0) {
  932.         register int i, j, w;
  933.         int columns, lines;
  934.  
  935.         lreply(214,
  936.       "The following commands are recognized (* =>'s unimplemented).");
  937.         columns = 76 / width;
  938.         if (columns == 0)
  939.             columns = 1;
  940.         lines = (NCMDS + columns - 1) / columns;
  941.         for (i = 0; i < lines; i++) {
  942.             printf("   ");
  943.             for (j = 0; j < columns; j++) {
  944.                 c = cmdtab + j * lines + i;
  945.                 printf("%s%c", c->name,
  946.                     c->implemented ? ' ' : '*');
  947.                 if (c + lines >= &cmdtab[NCMDS])
  948.                     break;
  949.                 w = strlen(c->name) + 1;
  950.                 while (w < width) {
  951.                     putchar(' ');
  952.                     w++;
  953.                 }
  954.             }
  955.             printf("\r\n");
  956.         }
  957.         (void) fflush(stdout);
  958.         reply(214, "Direct comments to ftp-bugs@@%s.", hostname);
  959.         return;
  960.     }
  961.     upper(s);
  962.     c = lookup(s);
  963.     if (c == (struct tab *)0) {
  964.         reply(502, "Unknown command %s.", s);
  965.         return;
  966.     }
  967.     if (c->implemented)
  968.         reply(214, "Syntax: %s %s", c->name, c->help);
  969.     else
  970.         reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
  971. }
  972. @
  973.  
  974.  
  975. 1.4
  976. log
  977. @Allowed users to use complex paths.
  978. @
  979. text
  980. @d707 4
  981. @
  982.  
  983.  
  984. 1.3
  985. log
  986. @Enabled writing to ftp directories.
  987. @
  988. text
  989. @d207 4
  990. a210 1
  991.     |    STOR check_login SP pathname_perm CRLF
  992. d294 4
  993. a297 1
  994.     |    XMKD check_login SP pathname_perm CRLF
  995. d485 1
  996. a485 1
  997.         if (guest && $1 && (strchr($1,'/') ||
  998. d487 1
  999. a487 1
  1000.             reply(550, "Sorry, guest must use simple paths.");
  1001. @
  1002.  
  1003.  
  1004. 1.2
  1005. log
  1006. @Got anonymous ftp working.
  1007. @
  1008. text
  1009. @d207 1
  1010. a207 1
  1011.     |    STOR check_login_guest SP pathname_perm CRLF
  1012. d291 1
  1013. a291 1
  1014.     |    XMKD check_login_guest SP pathname_perm CRLF
  1015. d327 1
  1016. a327 1
  1017.             reply(221, "Goodbye.  Be excellent to each other.");
  1018. d482 1
  1019. d499 1
  1020. @
  1021.  
  1022.  
  1023. 1.1
  1024. log
  1025. @Initial revision
  1026. @
  1027. text
  1028. @d55 1
  1029. d104 1
  1030. a104 1
  1031.                   "Guest login ok, send ident as password.");
  1032. d200 1
  1033. a200 1
  1034.     |    RETR check_login SP pathname CRLF
  1035. d207 1
  1036. a207 1
  1037.     |    STOR check_login SP pathname CRLF
  1038. d214 1
  1039. a214 1
  1040.     |    APPE check_login SP pathname CRLF
  1041. d226 1
  1042. a226 1
  1043.     |    NLST check_login SP pathname CRLF
  1044. d238 1
  1045. a238 1
  1046.     |    LIST check_login SP pathname CRLF
  1047. d245 1
  1048. a245 1
  1049.     |    DELE check_login SP pathname CRLF
  1050. d252 1
  1051. a252 1
  1052.     |    RNTO SP pathname CRLF
  1053. d291 1
  1054. a291 1
  1055.     |    XMKD check_login SP pathname CRLF
  1056. d298 1
  1057. a298 1
  1058.     |    XRMD check_login SP pathname CRLF
  1059. d315 1
  1060. a315 1
  1061.     |    STOU check_login SP pathname CRLF
  1062. d327 1
  1063. a327 1
  1064.             reply(221, "Goodbye.");
  1065. d336 1
  1066. a336 1
  1067. rcmd:        RNFR check_login SP pathname CRLF
  1068. d474 15
  1069. d492 15
  1070. d509 1
  1071. a509 1
  1072.         if (logged_in)
  1073. d511 1
  1074. a511 1
  1075.         else {
  1076. @
  1077.